package com.jgp.timing.service;

import com.jgp.common.utils.JGPUtil;
import com.jgp.sys.ui.Pager;
import com.jgp.sys.ui.Result;
import com.jgp.sys.utils.SpringContextUtil;
import com.jgp.timing.annotation.JobServiceMethod;
import com.jgp.timing.component.QuartzManager;
import com.jgp.timing.model.TaskTargetType;
import com.jgp.timing.model.TimingJob;
import com.jgp.timing.repository.JobRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.ThreadContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 项目   jgp-cloud-parent
 * 作者   loufei
 * 时间   2019/3/31
 */
@Slf4j
@Service
public class JobActionService {
    
    
    @Autowired
    private RestTemplate restTemplate;
    
    @Autowired
    private QuartzManager quartzManager;
    
    @Autowired
    private JobRepository jobRepository;
    
    @Transactional
    public TimingJob createJob(TimingJob job) {
        return jobRepository.create(job);
    }
    
    @Transactional
    public TimingJob updateJob(TimingJob job) {
        return jobRepository.update(job);
    }
    
    public void start(Long jobId) {
        TimingJob job = jobRepository.read(jobId);
        if(Objects.nonNull(job.getStarted())&&job.getStarted()){
            log.info("{}已经启动",job.getName());
            return ;
        }
        Map<Object, Object> resources = ThreadContext.getResources();
        quartzManager.addJob(jobId + "", job.getCron(), jobExecutionContext -> {
            ThreadContext.setResources(resources);
            boolean success = false;
            if (TaskTargetType.URL.equals(job.getType())) {
                Map<String, Object> param = JGPUtil.jsonToMap(job.getJsonParams());
                if(Objects.isNull(param)){
                    log.warn("[{}]任务参数解析错误:{}", job.getName(), job.getJsonParams());
                }
                Result result = restTemplate.postForObject(job.getTarget(), param, Result.class);
                success = result.getFlag();
                if(!success){
                    log.warn("{}服务调用失败\n\t{}", job.getName(), job.getTarget());
                }
            } else if (TaskTargetType.SERVICE.equals(job.getType())) {
                int splitIndex = job.getTarget().lastIndexOf(".");
                String serviceName = job.getTarget().substring(0, splitIndex);
                String methodName = job.getTarget().substring(splitIndex+1);
                try {
                    Class targetClass = Class.forName(serviceName);
                    Object target = SpringContextUtil.getBean(targetClass);
                    Method[] methods = targetClass.getDeclaredMethods();
                    Method targetMethod = null;
                    for (Method method : methods) {
                        JobServiceMethod jobServiceMethod = method.getAnnotation(JobServiceMethod.class);
                        if (Objects.nonNull(jobServiceMethod) && jobServiceMethod.value().equals(methodName)) {
                            targetMethod = method;
                            break;
                        }
                    }
                    if (Objects.nonNull(targetMethod)) {
                        targetMethod.setAccessible(true);
                        Object args = null;
                        if (targetMethod.getParameterCount() == 1) {
                            Class firstParamType = targetMethod.getParameterTypes()[0];
                            args = JGPUtil.toBean(job.getJsonParams(), firstParamType);
                        }
                        if(Objects.nonNull(args)){
                            targetMethod.invoke(target,args);
                        }else {
                            targetMethod.invoke(target);
                        }
                    }
                    success = true;
                } catch (ClassNotFoundException e) {
                    log.warn("{}服务调用失败\n\t找不到{}", job.getName(), serviceName,e);
                    e.printStackTrace();
                } catch (IllegalAccessException | InvocationTargetException e) {
                    log.warn("{}服务调用失败\n\t{}调用{}失败", job.getName(), serviceName,methodName);
                    e.printStackTrace();
                }
    
            }
            if(success){
                updateStatus(job);
                log.info("线程:{},任务:{},[{}]服务调用成功\n\t说明{}", jobExecutionContext.getFireInstanceId(),job.getName(),job.getTarget(), job.getRemark());
            }
        });
    }
    @Transactional
    public void updateStatus(TimingJob job) {
        if (Objects.isNull(job.getStarted())||!job.getStarted()) {
            job.setStarted(true);
        }
        Long times = job.getTimes();
        if (Objects.isNull(times)) {
            times = 0l;
        }
        job.setTimes(times+1l);
        jobRepository.update(job,"started","times");
    }
    
    public void restart(Long jobId) {
        TimingJob job = jobRepository.read(jobId);
        if(job.getStarted()){
            quartzManager.updateJob(jobId + "",job.getCron());
        }
    }
    
    @Transactional
    public void stop(Long jobId) {
        if(quartzManager.hasJob(jobId+"")){
            quartzManager.deleteJob(jobId + "");
        }
        TimingJob job = jobRepository.read(jobId);
        if(job.getStarted()){
            job.setStarted(false);
            jobRepository.update(job);
        }
        log.info("[{}]任务停止",job.getName());
    }
    
    @Transactional
    public boolean remove(Long jobId) {
        boolean flag = quartzManager.deleteJob(jobId + "");
        if(flag){
            jobRepository.delete(jobId);
            return true;
        }
        return false;
    }
    
    
    public List<TimingJob> queryAll(Pager pager) {
        return jobRepository.read(null,pager);
    }
    
    public TimingJob queryJob(Long jobId) {
        return jobRepository.read(jobId);
    }
}
